<template>
  <div class="drag-drop-svg-container">
    <div class="drag-drop-svg-left-panel" v-if="treeData.length > 0">

      <div style="padding: 10px;">
        <!-- pre train -->
        预处理算子
        <el-popover placement="top-start" title="提示" width="300" trigger="hover"
          content="点击树上带有齿轮图标的算法版本，添加算子。点击画布上的算子可以配置参数。">
          <div slot="reference" style="float: right; font-size: 20px;color: var(--app-primary-base);">
            <i class="el-icon-question"></i>
          </div>
        </el-popover>
      </div>
      <el-input placeholder="输入关键字进行过滤" v-model="filterText">
      </el-input>
      <el-tree ref="workFlowTree" :data="treeData" :props="{
        children: 'children',
        label: 'name'
      }" @node-click="nodeClick" :filter-node-method="filterNode" default-expand-all highlight-current>
        <span class="custom-tree-node" slot-scope="{ node, data }" :title="node.level == 3 ? '拖拽添加算子' : ''"
          :draggable="node.level == 3" @dragstart="handleDragStart($event, node, data)" @dragend="handleDragEnd">
          <span>
            <i v-if="node.level == 3" class="el-icon-setting" style="margin-right: 5px;"></i>
            {{ node.label }}
          </span>
        </span></el-tree>
    </div>
    <div ref="leftPanel" class="drag-drop-svg-middle-panel">
      <svg ref="sourceRef" id="sourceSvg"></svg>
    </div>

    <div ref="rightPanel" class="drag-drop-svg-right-panel">
      <svg ref="targetRef" id="targetSvg">
        <g class="drag-drop-svg-viewport"></g>
      </svg>
    </div>

    <Drawer v-if="showNodeDialog" :data="currentNodeData" @close="closeDrawer" @confirm="onDrawerConfirm"
      @nodeSubmit="nodeSubmit" @delNode="delNode" />
  </div>
</template>

<script>
import {
  getAlgorithmDetailsApi,
} from "@/api/myspace/preprocessing/index";
import { select, selectAll } from "d3-selection";
import { zoomIdentity, zoom as d3Zoom } from "d3-zoom";
import { drag } from "d3-drag";
import Drawer from "./Drawer/index.vue";
import {
  sourceGraph,
  createMarkerDefs,
  createNodeShape,
} from "./utils/svgElements.js";
import {
  getSmoothPath,
  findPortElement,
  getPortPosition,
  updateConnections,
  drawLine,
} from "./utils/algorithm";
import { transformationInvert } from "design-mei";

let ghost = null;
let selectedPort = null;
let tempLine = null;
let isConnecting = false;
let selectedNode = null;
let selectedConnection = null;
let currentTransform = zoomIdentity;
let connections = [];
let nodeList = [];

export default {
  name: "Workflow",
  components: { Drawer },
  props: {
    treeData: {
      type: Array,
      default: [],
    },
    initData: {
      type: String,
      default: () => ('')
    }
  },
  watch: {
    filterText(val) {
      this.$refs.workFlowTree.filter(val);
    }
  },
  data() {
    return {
      filterText: "",
      suanZiList: [],
      isEdit: false,
      showNodeDialog: false,
      currentNodeData: null,
      editingNodeId: null,
    };
  },
  mounted() {
    nodeList = [];
    connections = [];
    this.$refs.targetRef.appendChild(createMarkerDefs());
    // const svg = select(this.$refs.sourceRef);
    // this.suanZiList = this.extractNodesAtLevel(this.treeData);
    // this.suanZiList.forEach((item, index) => {
    //   let rep = {
    //     type: "rect",
    //     x: 50,
    //     y: 20 + index * 50,
    //     width: 100,
    //     height: 30,
    //     label: item.data.name,
    //     fill: "#EFF4FF",
    //     stroke: "#5F95FF",
    //     params: item.data,
    //   }
    //   createNodeShape(svg, rep);
    // });
    // 确保 currentTransform 有正确的初始值
    if (!currentTransform) {
      currentTransform = zoomIdentity;
    }

    const markerDefs = createMarkerDefs();
    this.$refs.targetRef.appendChild(markerDefs);
    this.initDrag();
    this.initDropZone(); // 添加这行
    setTimeout(() => {
      // 如果有初始数据，则加载渲染
      if (this.initData) {
        try {
          const parsedData = JSON.parse(this.initData);
          this.renderGraph(parsedData);
        } catch (error) {
          console.error('解析初始数据失败:', error);
        }
      } else if (this.isEdit) {
        this.loadGraphData();
      }
    }, 1000);
  },
  methods: {
    // 新增拖拽开始处理
    // 新增拖拽开始处理
    handleDragStart(event, node, data) {
      if (node.level !== 3) return;

      // 设置拖拽数据，只传递必要信息，避免循环引用
      const dragData = {
        nodeId: node.id,
        label: node.label,
        data: data  // data 是树节点的原始数据，通常不包含循环引用
      };
      event.dataTransfer.setData('text/plain', JSON.stringify(dragData));
      event.dataTransfer.effectAllowed = 'copy';

      // 创建拖拽预览图像
      const ghost = document.createElement('div');
      ghost.style.cssText = `
    position: absolute;
    top: -1000px;
    background: #EFF4FF;
    border: 1px solid #5F95FF;
    padding: 5px 10px;
    border-radius: 4px;
    font-size: 12px;
    pointer-events: none;
    z-index: -1;
  `;
      ghost.innerHTML = node.label;
      document.body.appendChild(ghost);

      event.dataTransfer.setDragImage(ghost, 0, 0);

      // 使用 setTimeout 确保 setDragImage 生效后移除
      setTimeout(() => {
        document.body.removeChild(ghost);
      }, 0);
    },

    handleDragEnd(event) {
      // 拖拽结束清理
    },
    // 添加初始化 drop zone 的方法
    initDropZone() {
      const targetSvg = select(this.$refs.targetRef);

      targetSvg
        .attr('draggable', true)
        .on('dragover', (event) => {
          event.preventDefault();
          event.dataTransfer.dropEffect = 'copy';
        })
        .on('drop', (event) => {
          event.preventDefault();
          event.stopPropagation();
          const dragData = JSON.parse(event.dataTransfer.getData('text/plain'));

          // 创建一个模拟的事件对象用于坐标转换
          const mockEvent = {
            clientX: event.clientX,
            clientY: event.clientY
          };
          console.log('old:',mockEvent.clientX,mockEvent.clientY)
          // 使用 transformationInvert 获取 SVG 坐标
          const [x, y] = transformationInvert(mockEvent, targetSvg, currentTransform);
          
          const position = {
            x: x,
            y: y
          };
          console.log('new:', position.x,position.y);
          // 调用 API 获取详细信息并创建节点
          getAlgorithmDetailsApi(dragData.data.data.refId).then((response) => {
            if (response.code == 200) {
              this.createNodeFromTree(response.data, 'pre', position);
            }
          });
        });
    },
    // 删除节点
    delNode(id) {
      // 查找对应 ID 的节点
      const nodeIndex = nodeList.findIndex(node => node.id === id);

      if (nodeIndex !== -1) {
        const nodeData = nodeList[nodeIndex];

        // 1. 删除与该节点相关的所有连线
        // 先从 DOM 中删除这些连线
        connections
          .filter(c => c.from.nodeId === id || c.to.nodeId === id)
          .forEach(c => {
            select(c.path).remove();
          });

        // 从 connections 数组中移除这些连线
        for (let i = connections.length - 1; i >= 0; i--) {
          if (connections[i].from.nodeId === id || connections[i].to.nodeId === id) {
            connections.splice(i, 1);
          }
        }

        // 2. 从 DOM 中删除节点
        const targetSvg = select(this.$refs.targetRef);
        const viewport = targetSvg.select(".drag-drop-svg-viewport");

        viewport
          .selectAll("g")
          .filter(function (d) {
            return d && d.id === id;
          })
          .remove();

        // 3. 从 nodeList 中移除节点数据
        nodeList.splice(nodeIndex, 1);

        // 4. 如果删除的是当前选中的节点，清理选中状态
        if (selectedNode) {
          const selectedNodeData = select(selectedNode).datum();
          if (selectedNodeData && selectedNodeData.id === id) {
            selectedNode = null;
            this.showNodeDialog = false;
            this.editingNodeId = null;
            this.currentNodeData = null;
          }
        }

        // 5. 触发图形数据更新事件
        this.emitGraphData();

        console.log(`节点 ${id} 及其相关连线已删除`);
      } else {
        console.warn(`未找到 ID 为 ${id} 的节点`);
      }
    },
    // 获取数据
    getData() {
      return {
        nodes: nodeList,
        connections: connections,
      };
    },
    // 节点更新
    nodeSubmit(id, label, args) {
      // 查找对应 ID 的节点
      const nodeIndex = nodeList.findIndex(node => node.id === id);

      if (nodeIndex !== -1) {
        // 更新节点的 args 数据
        nodeList[nodeIndex].data.argMap = args;
        nodeList[nodeIndex].label = label;
        // 更新图形元素上的 data 属性（如果存在）
        const targetNode = select(this.$refs.targetRef)
          .select(".drag-drop-svg-viewport")
          .selectAll("g")
          .filter(d => d && d.id === id);

        // 如果需要更新节点显示，可以在这里添加相关逻辑

        // 触发图形数据更新事件
        this.emitGraphData();

        console.log(`节点 ${id} 的 argMap 已更新:`, args);
        this.closeDrawer();
      } else {
        console.warn(`未找到 ID 为 ${id} 的节点`);
      }
    },
    filterNode(value, data) {
      if (!value) return true;
      return data.name.indexOf(value) !== -1;
    },
    nodeClick(node) {
      // 处理算法详情
      // if (node.data.nodeType == 1) {
      //   getAlgorithmDetailsApi(node.data.refId).then((response) => {
      //     if (response.code == 200) {
      //       this.createNodeFromTree(response.data, 'pre');
      //     }
      //   })
      // }
    },
    // 修改 createNodeFromTree 方法，添加位置参数
    createNodeFromTree(data, type, position = null) {
      // console.log("=创建节点=")
      const targetSvg = select(this.$refs.targetRef);
      const viewport = targetSvg.select(".drag-drop-svg-viewport");

      // 如果提供了位置，则使用该位置；否则使用原来的逻辑
      let transformedPos;
      if (position) {
        // 直接使用已经转换好的坐标
        transformedPos = {
          x: position.x,
          y: position.y
        };
      } else {
        // 原有逻辑
        const viewBox = this.$refs.targetRef.viewBox.baseVal;
        const centerX = viewBox.width / 2 + 50;
        const centerY = viewBox.height / 2 + 20;

        transformedPos = {
          x: centerX + nodeList.length * 5,
          y: centerY + nodeList.length * 5
        };
      }
      // 处理 startupParam 数据
      let parsedStartupParam = '{}';
      try {
        if (data.startupParam) {
          parsedStartupParam = JSON.parse(data.startupParam);
        }
      } catch (e) {
        this.$message.error('算子解析有误');
        return;
      }
      // 创建节点数据
      const newNodeData = {
        id: Math.random().toString(36).slice(2, 8),
        label: data.name,
        type: "rect", // 或根据需要设置其他类型
        x: transformedPos.x,
        y: transformedPos.y,
        width: 100,
        height: 30,
        params: parsedStartupParam,
        data: {
          id: data.id,
          type: type,// 预处理pre 训练train
          image: data.imageNameInRepo || '',
          commandStr: data.startupCommand || '',
          env: data.envVariableList || [],
          argMap: data.argMap || {}
        }, // 保存原始数据
      };

      // 创建节点图形
      const nodeGroup = createNodeShape(viewport, {
        ...newNodeData,
        fill: "#EFF4FF",
        stroke: "#5F95FF"
      });

      if (nodeGroup) {
        nodeGroup.attr("transform", `translate(${newNodeData.x}, ${newNodeData.y})`);
        nodeGroup.datum(newNodeData);
        nodeList.push(newNodeData);
        this.addHandler(nodeGroup.node());
        this.emitGraphData();
      }
    },
    /**
     * 提取指定层级节点
     */
    extractNodesAtLevel(treeData, level = 3) {
      const resultNodes = [];

      const traverse = (nodes, currentLevel) => {
        if (!Array.isArray(nodes)) return;

        nodes.forEach(node => {
          if (currentLevel === level) {
            resultNodes.push(node);
          } else if (node.children && node.children.length > 0) {
            traverse(node.children, currentLevel + 1);
          }
        });
      };

      traverse(treeData, 1);
      return resultNodes;
    },
    initDrag() {
      const vm = this;
      const sourceSvg = select(this.$refs.sourceRef);
      const targetSvg = select(this.$refs.targetRef);
      const viewport = targetSvg.select(".drag-drop-svg-viewport");

      const zoom = d3Zoom()
        .scaleExtent([0.5, 2])
        .filter(function (event) {
          if (event.type === "dblclick") return false;
          return !event.ctrlKey || event.type === "wheel";
        })
        .on("zoom", (event) => {
          currentTransform = event.transform;
          viewport.attr("transform", event.transform);
        });

      targetSvg.call(zoom);

      targetSvg.on("wheel", (event) => {
        if (event.ctrlKey) {
          event.preventDefault();
        }
      });

      // 删除节点和相关连线
      select(window).on("keydown", function (event) {
        // 删除选中的节点

        const activeEl = document.activeElement;
        const isInput =
          activeEl &&
          (activeEl.tagName === "INPUT" ||
            activeEl.tagName === "TEXTAREA" ||
            activeEl.isContentEditable);

        // 保险起见，仍然避免在输入中触发
        if (isInput) return;
        if (
          (event.key === "Backspace" || event.key === "Delete") &&
          selectedNode
        ) {
          const nodeData = select(selectedNode).datum();
          const index = nodeList.findIndex((n) => n.id === nodeData.id);
          if (index > -1) {
            nodeList.splice(index, 1);
          }

          connections
            .filter(
              (c) =>
                c.from.nodeId === nodeData.id || c.to.nodeId === nodeData.id
            )
            .forEach((c) => {
              select(c.path).remove();
            });

          for (let i = connections.length - 1; i >= 0; i--) {
            if (
              connections[i].from.nodeId === nodeData.id ||
              connections[i].to.nodeId === nodeData.id
            ) {
              connections.splice(i, 1);
            }
          }

          select(selectedNode).remove();
          selectedNode = null;
          vm.showNodeDialog = false;
          vm.emitGraphData();
        }

        // 删除选中的连接线
        if (
          (event.key === "Backspace" || event.key === "Delete") &&
          selectedConnection
        ) {
          const pathEl = selectedConnection;

          // 从 DOM 删除路径
          select(pathEl).remove();

          // 从 connections 数组中移除
          for (let i = connections.length - 1; i >= 0; i--) {
            if (connections[i].path === pathEl) {
              connections.splice(i, 1);
              break;
            }
          }
          selectedConnection = null;
          vm.emitGraphData();
        }
      });

      targetSvg.on("click", function () {
        if (selectedNode) {
          selectAll("#targetSvg .drag-drop-svg-viewport > g *")
            .filter(function () {
              return this.tagName !== "text";
            })
            .attr("stroke", "#5F95FF");
          select(selectedNode).select("*").attr("filter", null);
          selectedNode = null;
          vm.showNodeDialog = false;
        }

        if (selectedConnection) {
          select(selectedConnection)
            .classed("selected-connection", false)
            .attr("stroke", "#A2B1C3")
            .attr("stroke-dasharray", null);
          selectedConnection = null;
        }
      });
      sourceSvg.selectAll(".draggable").each(function () {
        const g = select(this);
        const bbox = this.getBBox();
        // ox, oy 表示元素在源 SVG 中的原始位置
        const data = {
          ox: bbox.x,
          oy: bbox.y,
          width: bbox.width,
          height: bbox.height,
        };
        g.datum(data).call(
          drag().on("start", dragstarted).on("drag", dragged).on("end", dropped)
        );
      });

      function dragstarted(event, d) {
        const bbox = this.getBBox();
        const matrix = this.getScreenCTM();
        const width = bbox.width * matrix.a;
        const height = bbox.height * matrix.d;
        const scale = currentTransform.k;

        ghost = document.createElement("div");
        ghost.classList.add("drag-ghost");
        ghost.style.left = event.sourceEvent.clientX - 10 + "px";
        ghost.style.top = event.sourceEvent.clientY - 10 + "px";
        ghost.style.width = width + "px";
        ghost.style.height = height + "px";
        const svgClone = document.createElementNS(
          "http://www.w3.org/2000/svg",
          "svg"
        );
        svgClone.setAttribute("width", width * scale);
        svgClone.setAttribute("height", height * scale);
        svgClone.setAttribute(
          "viewBox",
          `${bbox.x} ${bbox.y} ${bbox.width} ${bbox.height}`
        );

        const elementClone = this.cloneNode(true);
        svgClone.appendChild(elementClone);
        ghost.appendChild(svgClone);
        document.body.appendChild(ghost);
      }

      function dragged(event, d) {
        if (ghost) {
          ghost.style.left = event.sourceEvent.clientX - 10 + "px";
          ghost.style.top = event.sourceEvent.clientY - 10 + "px";
        }
      }

      function dropped(e, d) {
        if (ghost && document.body.contains(ghost)) {
          document.body.removeChild(ghost);
          ghost = null;
        }
        const [mx, my] = transformationInvert(e, targetSvg, currentTransform);
        // 计算偏移量
        const shiftX = mx - d.ox;
        const shiftY = my - d.oy;

        // 克隆当前元素
        const clonedElement = this.cloneNode(true);
        const nodeId = Math.random().toString(36).slice(2, 8);
        const nodeData = {
          id: nodeId,
          label: this.getAttribute("data-label"),
          type: this.getAttribute("data-type"),
          x: shiftX,
          y: shiftY,
          width: d.width,
          height: d.height,
          data: {}, // 后续往里面增加数据
        };
        nodeList.push(nodeData);

        select(clonedElement).datum(nodeList[nodeList.length - 1]);
        clonedElement.setAttribute(
          "transform",
          `translate(${shiftX}, ${shiftY})`
        );
        viewport.node().appendChild(clonedElement);
        vm.addHandler(clonedElement);
        vm.emitGraphData();
      }
    },

    // 编辑
    async loadGraphData() {
      //   {
      //   id: string,        // 节点唯一标识，必须
      //   type: string,      // 节点类型，比如 'rect', 'polygon', 'circle'，必须
      //   label: string,     // 节点显示的文字标签，必须
      //   x: number,         // 节点 x 坐标，必须
      //   y: number,         // 节点 y 坐标，必须
      //   width?: number,    // 节点宽度（rect类型），rect必须
      //   height?: number,   // 节点高度（rect类型），rect必须
      //   points?: string,   // polygon类型顶点坐标，polygon必须
      //   cx?: number,       // circle类型圆心x，circle必须
      //   cy?: number,       // circle类型圆心y，circle必须
      //   r?: number,        // circle类型半径，circle必须
      //   data?: object      // 额外数据，选填
      // }
      const data = await new Promise((resolve) =>
        resolve({
          nodes: [
            {
              id: "node-1",
              type: "rect",
              label: "开始",
              x: 100,
              y: 100,
              width: 66,
              height: 36,
              data: { label: "hhhhh" },
            },
            {
              id: "node-2",
              type: "polygon",
              label: "决策",
              x: 300,
              y: 200,
              points: "50,0 100,25 50,50 0,25",
              data: { label: "kkk" },
            },
          ],
          connections: [
            {
              from: {
                nodeId: "node-1",
                port: "bottom",
              },
              to: {
                nodeId: "node-2",
                port: "top",
              },
            },
          ],
        })
      );
      this.renderGraph(data);
    },
    renderGraph(data) {
      const targetSvg = select(this.$refs.targetRef);
      const viewport = targetSvg.select(".drag-drop-svg-viewport");
      const fillColor = "#EFF4FF";
      const strokeColor = "#5F95FF";
      const vm = this;

      // 清空现有节点和连接
      nodeList.length = 0;
      while (connections.length > 0) {
        connections.pop();
      }

      // 清空SVG中的元素
      viewport.selectAll("*").remove();

      // 添加marker定义
      viewport.node().appendChild(createMarkerDefs());

      data.nodes.forEach((nodeData) => {
        let group;

        const commonProps = {
          fill: fillColor,
          stroke: strokeColor,
          ...nodeData,
        };

        group = createNodeShape(viewport, {
          ...commonProps,
        });

        if (!group) return;

        group.attr("transform", `translate(${nodeData.x}, ${nodeData.y})`);
        group.datum(nodeData);
        nodeList.push(nodeData);
        vm.addHandler(group.node());
      });

      // 渲染连线
      if (data.connections) {
        data.connections.forEach((conn) => {
          const fromPortEl = findPortElement(conn.from, viewport);
          const toPortEl = findPortElement(conn.to, viewport);

          if (fromPortEl && toPortEl) {
            // 确保 currentTransform 有值
            const transform = currentTransform || zoomIdentity;
            const s = getPortPosition(fromPortEl, targetSvg, transform);
            const e = getPortPosition(toPortEl, targetSvg, transform);

            const path = drawLine(viewport, s, e, selectedConnection);

            connections.push({
              from: conn.from,
              to: conn.to,
              path: path.node(),
            });
          } else {
            console.warn("无法找到端口元素:", conn.from, conn.to);
          }
        });
      }

      this.emitGraphData();
    },

    // 抽屉提交
    onDrawerConfirm(updatedData) {
      this.showNodeDialog = false;

      const index = nodeList.findIndex((n) => n.id === this.editingNodeId);
      if (index !== -1) {
        const node = nodeList[index];

        // 逐项更新属性
        node.label = updatedData.label;
        node.data = {
          ...node.data,
          ...updatedData.data,
        };

        // 更新图形
        const targetNode = select(this.$refs.targetRef)
          .select(".drag-drop-svg-viewport")
          .selectAll("g")
          .filter((d) => d && d.id === this.editingNodeId);

        targetNode.select("text").text(updatedData.label);
      }

      this.editingNodeId = null;
      this.currentNodeData = null;
      this.emitGraphData();
    },

    emitGraphData() {
      this.$emit("graph-update", {
        nodes: [...nodeList],
        connections: connections.map((c) => ({
          from: { ...c.from },
          to: { ...c.to },
        })),
      });
    },

    closeDrawer() {
      this.showNodeDialog = false;
      this.editingNodeId = null;
      this.currentNodeData = null;
    },
    startConnection(port, nodeData, portType) {
      const vm = this;
      const targetSvg = select(this.$refs.targetRef);
      const viewport = targetSvg.select(".drag-drop-svg-viewport");
      if (isConnecting) return;
      selectedPort = { port, nodeData, portType };
      isConnecting = true;

      // 确保 currentTransform 有值
      const transform = currentTransform || zoomIdentity;
      const start = getPortPosition(port, targetSvg, transform);

      tempLine = viewport
        .append("path")
        .attr("d", getSmoothPath(start, start))
        .attr("stroke", "#A2B1C3")
        .attr("stroke-width", 2)
        .attr("stroke-dasharray", "5,5")
        .attr("fill", "none")
        .attr("class", "temp-connection-line");

      targetSvg.on("mousemove.temp", (e) => {
        const [mx, my] = transformationInvert(e, targetSvg, transform);
        const fakeEnd = { x: mx, y: my };
        tempLine.attr("d", getSmoothPath(start, fakeEnd));
      });

      select(window).on("mouseup.temp", (e) => {
        const [mx, my] = transformationInvert(e, targetSvg, transform);

        // 查找最近的端口
        const targetPortEl = selectAll("#targetSvg .port")
          .filter(function () {
            const pos = getPortPosition(this, targetSvg, transform);
            return Math.hypot(pos.x - mx, pos.y - my) < 15; // 增大检测范围
          })
          .node();

        if (targetPortEl) {
          const targetPortType = targetPortEl.getAttribute("data-port");

          // 验证连接有效性
          if (
            selectedPort.portType === "bottom" &&
            targetPortType === "bottom"
          ) {
            console.warn("不允许底部连接到底部");
            vm.cleanupTempLine(targetSvg);
            return;
          }

          if (selectedPort.portType === "top" && targetPortType === "top") {
            console.warn("不允许顶部连接到顶部");
            vm.cleanupTempLine(targetSvg);
            return;
          }

          // 确保不是连接到同一个节点
          if (targetPortEl.parentNode === selectedPort.port.parentNode) {
            console.warn("不允许连接到同一个节点");
            vm.cleanupTempLine(targetSvg);
            return;
          }

          // 确保不是重复连接
          const targetNodeData = select(targetPortEl.parentNode).datum();
          const isDuplicate = connections.some(conn =>
            (conn.from.nodeId === selectedPort.nodeData.id && conn.to.nodeId === targetNodeData.id) ||
            (conn.from.nodeId === targetNodeData.id && conn.to.nodeId === selectedPort.nodeData.id)
          );

          if (isDuplicate) {
            console.warn("节点间已存在连接");
            vm.cleanupTempLine(targetSvg);
            return;
          }

          if (targetPortEl && targetPortEl !== selectedPort.port) {
            const s = getPortPosition(
              selectedPort.port,
              targetSvg,
              transform
            );
            const e = getPortPosition(targetPortEl, targetSvg, transform);

            // 连接 画线
            const finalPath = drawLine(viewport, s, e, selectedConnection).node();

            const targetNodeData = select(targetPortEl.parentNode).datum();
            const targetPortType = targetPortEl.getAttribute("data-port");

            connections.push({
              from: {
                nodeId: selectedPort.nodeData.id,
                label: selectedPort.nodeData.label,
                port: selectedPort.portType,
              },
              to: {
                nodeId: targetNodeData.id,
                label: targetNodeData.label,
                port: targetPortType,
              },
              path: finalPath,
            });
            vm.emitGraphData();
          }
        }

        vm.cleanupTempLine(targetSvg);
      });
    },
    addHandler(clonedElement) {
      const vm = this;
      const targetSvg = select(this.$refs.targetRef);
      const viewport = targetSvg.select(".drag-drop-svg-viewport");

      select(clonedElement).on("click", function (event, d) {
        event.stopPropagation();
        if (selectedNode) {
          select(selectedNode).select("*").attr("filter", null);
        }
        selectedNode = this;
        selectAll("#targetSvg .drag-drop-svg-viewport > g *")
          .filter(function () {
            return this.tagName !== "text";
          })
          .attr("stroke", "#5F95FF");

        select(selectedNode).select("*:not(text)").attr("stroke", "#239EDD"); // 设置选中颜色
        // 如果使用的是 data- 属性方式
        const paramsStr = select(this).attr("data-params");
        if (paramsStr) {
          const params = JSON.parse(paramsStr);
        }
        vm.currentNodeData = JSON.parse(JSON.stringify(d));
        vm.editingNodeId = d.id;
        // 连续点击开启面板时，先关闭面板再打开，解决数据不更新问题
        vm.showNodeDialog = false;
        setTimeout(() => {
          vm.showNodeDialog = true;
        }, 100);
      });

      select(clonedElement)
        .selectAll(".port")
        .style("visibility", "visible")
        .on("mousedown", function (evt) {
          evt.stopPropagation();
          const nodeData = select(this.parentNode).datum();
          const portType = select(this).attr("data-port");
          vm.startConnection(this, nodeData, portType);
        });

      // 给克隆的节点加拖拽
      select(clonedElement).call(
        drag()
          .on("start", function (e, d) {
            select(this).raise();
            const [x, y] = transformationInvert(e, targetSvg, currentTransform);
            d.ox = x - d.x;
            d.oy = y - d.y;
          })
          .on("drag", function (e, d) {
            const [x, y] = transformationInvert(e, targetSvg, currentTransform);
            d.x = x - d.ox;
            d.y = y - d.oy;
            select(this).attr("transform", `translate(${d.x}, ${d.y})`);
            updateConnections(
              d.id,
              connections,
              viewport,
              currentTransform,
              targetSvg
            );
          })
      );
    },
    cleanupTempLine(targetSvg) {
      if (tempLine) {
        tempLine.remove();
        tempLine = null;
      }
      selectedPort = null;
      isConnecting = false;

      // 清理事件监听器
      if (targetSvg) {
        targetSvg.on(".temp", null);
      }
      select(window).on(".temp", null);
    },
  },
  beforeDestroy() {
    if (ghost && document.body.contains(ghost)) {
      document.body.removeChild(ghost);
      ghost = null;
    }

    selectedPort = null;
    tempLine = null;
    isConnecting = false;
    selectedNode = null;
    selectedConnection = null;
    currentTransform = null;
  },
};
</script>

<style>
.drag-drop-svg-container {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
}

.drag-drop-svg-left-panel {
  width: 30%;
  height: 600px;
  border: 1px solid var(--ui-border-color-split);
  padding: 10px;
}

.drag-drop-svg-middle-panel {
  width: 0%;
  height: 100%;
  border: 1px solid var(--ui-border-color-split);
}

.drag-drop-svg-right-panel {
  width: 100%;
  height: 100%;
  border: 1px solid var(--ui-border-color-split);
}

svg {
  width: 100%;
  height: 100%;
}

/* .draggable {
  cursor: move;
} */

.drag-ghost {
  position: absolute;
  pointer-events: none;
  opacity: 0.7;
  /* border: 1px dashed #000; */
  /* background: rgba(255, 255, 255, 0.8); */
  z-index: 10000;
  /* box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); */
}

.drag-ghost svg {
  display: block;
}

.draggable text {
  font-family: Arial, sans-serif;
  font-size: 12px;
  fill: #262626;
  text-anchor: middle;
  dominant-baseline: middle;
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
}

#targetSvg {
  background-color: #101c30;
  height: 100%;
}
</style>
